﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Highlearner_API;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Logging;
using System.Threading;

namespace Highlearner
{
    public enum EHighlearnerState
    {
        Disconnected,
		Connecting,
		Idle,
		RefreshingCoursesList,
		RefreshingCourseItems,
		DownloadingCourse,
		Aborting,

// 		OfflineCoursesLoaded,
//      WaitingForConnection,
//      Connected,
//      WaitingForCoursesList,
//      CoursesListDownloaded,
//      WaitingForCourseDescription,
// 		CourseDescriptionReceived,
//      DownloadingItems,
// 		DownloadFinished,
// 
// 		DownloadingCourseListFailed,
//      DownloadingCourseDescriptionFailed,
//      DownloadingItemsFailed
    }

	public enum EHighlearnerError
	{
		ConnectingFailed,
		NetworkError,
		RefreshCoursesListFailed,
		RefreshCourseDetailsFailed,
		FilesFailedDownloading
	}

	public delegate void ErrorDelegate(EHighlearnerError error);
	public delegate void StateChangedDelegate(EHighlearnerState newState, string message);
	public delegate void DownloadProgressDelegate(int totalFilesCount, int downloadedFilesCount, CFileItem a_oCurrentFile);


    public class CManager
    {
		private CDataCenter m_oDataCenter;
		public Highlearner_API.CDataCenter DataCenter
		{
			get { return m_oDataCenter; }
		}

        private CExecuter m_oExecuter;
		public CExecuter Executer
		{
			get { return m_oExecuter; }
		}

		private EHighlearnerState m_eHighlearnerState;
		public Highlearner.EHighlearnerState HighlearnerState
		{
			get { return m_eHighlearnerState; }
		}

		int totalNumberOfFiles = 0;
		// Number of files downloaded - NOTE: Files that weren't downloaded successfully will still be counted here
		int numberOfDownloadedFiles = 0;

		public List<CFileItem> FailedFiles = new List<CFileItem>();

        public event StateChangedDelegate OnStateChanged;
		public event DownloadProgressDelegate OnDownloadProgress;
		public event ErrorDelegate OnError;


		private Thread m_oConnectThread;
		private Thread m_oRefreshCoursesListThread;
		private Thread m_oRefreshCourseDetailsThread;

        public void Initialize()
        {
			InitializeExecuter();
			SetState(EHighlearnerState.Disconnected);
        }

		private void InitializeExecuter()
		{
			m_oDataCenter = new CDataCenter();
			m_oExecuter = new CExecuter(DataCenter);

			// Set the corresponding core for the current university
			Executer.Core = CInstitutes.GetAPI((EInstitute)Properties.Settings.Default.Institute);

			Executer.SavePath = Properties.Settings.Default.SavePath;

// 			Executer.OnConnectFinished += new ConnectFinishedDelegate(CExecuterConnectFinished);
// 			Executer.OnDisconnected += new SimpleDelegate(CExecuterDisconnected);
// 			Executer.OnCoursesLoadedEvent += new SimpleDelegate(CExecuterCoursesLoaded);
			Executer.OnFileDownloadStarted += new FileDownloadDelegate(Executer_OnFileDownloadStarted);
			Executer.OnFileDownloadEndedSuccessfully += new FileDownloadDelegate(Executer_OnFileDownloadEndedSuccessfully);
			Executer.OnFileDownloadFailed += new FileDownloadDelegate(Executer_OnFileDownloadFailed);
// 			Executer.OnCourseDownloadStarted += new CourseDownloadStarted(CourseDownloadStarted);
// 			Executer.OnCourseDownloadEnded += new CourseDownloadEnded(CourseDownloadEnded);
			Executer.OnDownloadStarted += new DownloadStarted(Executer_OnDownloadStarted);
			Executer.OnDownloadEnded += new DownloadEnded(Executer_OnDownloadEnded);

			// Load what you can
			LoadCoursesFromFile();
		}

		void Executer_OnDownloadStarted(int a_iNumberOfFiles)
		{
			totalNumberOfFiles = a_iNumberOfFiles;
		}

		void Executer_OnDownloadEnded()
		{
			SetState(EHighlearnerState.Idle, "ההורדה הסתיימה");

			if (FailedFiles.Count > 0)
			{
				ReportError(EHighlearnerError.FilesFailedDownloading);
			}
		}

		void Executer_OnFileDownloadFailed(CFileItem a_oFile)
		{
			FailedFiles.Add(a_oFile);
		}

		void Executer_OnFileDownloadEndedSuccessfully(CFileItem a_oFile)
		{
		}

		void Executer_OnFileDownloadStarted(CFileItem a_oFile)
		{
			numberOfDownloadedFiles++;
			ReportProgress(a_oFile);
		}

		void ReportProgress(CFileItem a_oFile)
		{
			if (OnDownloadProgress != null)
				OnDownloadProgress(totalNumberOfFiles, numberOfDownloadedFiles, a_oFile);
		}

		#region Error Reporting Methods
		private void ReportError(EHighlearnerError a_eError)
		{
			ReportError(a_eError, "");
		}
		private void ReportError(EHighlearnerError a_eError, string a_sMessage)
		{
			if (OnError != null)
				OnError(a_eError);
		}
		private void ReportErrorConnectingFailed()
		{
			ReportError(EHighlearnerError.ConnectingFailed);
		}
		private void ReportErrorNetworkError()
		{
			Disconnect();
			ReportError(EHighlearnerError.NetworkError);
		}
		private void ReportErrorRefreshCourseDetailsFailed()
		{
			ReportError(EHighlearnerError.RefreshCourseDetailsFailed);
		}
		#endregion

		#region CExecuter Events

// 		void CExecuterDisconnected()
// 		{
// 			SetState(EHighlearnerState.Disconnected);
// 		}
// 
// 		void CourseDownloadStarted(Course a_oCourse, int a_iNumberOfItems)
// 		{
// 			totalNumberOfFiles = a_iNumberOfItems;
// 		}
// 		void CourseDownloadEnded(Course a_oCourse)
// 		{
// 		}
// 
// 		void DownloadStarted()
// 		{
// 			SetState(EHighlearnerState.DownloadingItems);
// 		}
// 		void DownloadEnded()
// 		{
// 			SetState(EHighlearnerState.DownloadFinished);
// 
// 			SetState(EHighlearnerState.Connected, "ההורדה הסתיימה");
// 
// 			System.Diagnostics.Process.Start(Executer.Core.SavePath);
// 		}
// 
// 		void FileDownloadFailed(Course a_oCourse, CFolderItem a_oFolder, CFileItem a_oFile)
// 		{
// 			//m_oCore.Core.logger.WriteLn("File " + a_oFolder.Name + "\\" + a_oFile.Name + " failed downloading");
// 
// 			try
// 			{
// 				//toolStripProgressBar.ProgressBar.Value++;
// 				numberOfDownloadedFiles++;
// 				ReportProgress();
// 			}
// 			catch (Exception e)
// 			{
// 				CLogger.ReportException(e);
// 			}
// 		}
// 		void FileDownloadEndedSuccessfully(Course a_oCourse, CFolderItem a_oFolder, CFileItem a_oFile)
// 		{
// 			// Thread Safe Updating
// 			//this.Invoke(new FileDownloadDelegate(FileDownloadEndedSuccessfullySafe), a_oCourse, a_oFolder, a_oFile);
// 
// 			try
// 			{
// 				//toolStripProgressBar.ProgressBar.Value++;
// 				numberOfDownloadedFiles++;
// 				ReportProgress();
// 			}
// 			catch (Exception e)
// 			{
// 				CLogger.ReportException(e);
// 			}
// 		}
// 		void FileDownloadStarted(Course a_oCourse, CFolderItem a_oFolder, CFileItem a_oFile)
// 		{
// // 			toolStripStatusLabelCourseName.Text = a_oCourse.Name + "    - ";
// // 
// // 			toolStripStatusLabelCurrentStatus.Text = "מוריד את: " + a_oFile.Name;
// 		}
// 
// 		private void CExecuterCoursesLoaded()
// 		{
// 			SetState(EHighlearnerState.CoursesListDownloaded);
// 		}
// 
		#endregion

		#region Helper Methods
		/// <summary>
		/// Loads courses data from a file
		/// </summary>
		public void LoadCoursesFromFile()
		{
			CLogger.LogFunctionEntry();
			if (HighlearnerState != EHighlearnerState.Idle)
			{
				CLogger.LogFunction("State is not Idle - Aborting...");
				CLogger.LogFunctionExit();
				return;
			}
			string l_sCoursesFileFilename = CUtilities.GetCoursesFileFilename(Properties.Settings.Default.Username);
			// Load the previously saved courses from file
			if (File.Exists(l_sCoursesFileFilename))
			{
				FileStream l_oStream = File.Open(l_sCoursesFileFilename, FileMode.Open);
				BinaryFormatter l_oBinFormatter = new BinaryFormatter();

				m_oDataCenter = (CDataCenter)l_oBinFormatter.Deserialize(l_oStream);

				l_oStream.Close();
			}

			DataCenter.SaveFilename = l_sCoursesFileFilename; ;
			DataCenter.AllowSaving = Properties.Settings.Default.SaveUserCourses;
			
			CLogger.LogFunctionExit();
		}
		#endregion

		private void SetState(EHighlearnerState a_eNewState, string a_sMessage = "")
		{
			m_eHighlearnerState = a_eNewState;

			switch (HighlearnerState)
			{
				default:
					break;
			}

			if (OnStateChanged != null)
				OnStateChanged(a_eNewState, a_sMessage);
		}


		private void ConnectThreadFunc()
		{
			SetState(EHighlearnerState.Connecting);

			// Get the authentication details from the settings
			Executer.CONFIG_USERNAME = Properties.Settings.Default.Username;
			Executer.CONFIG_PASSWORD = Utilities.CAESEncyption.Decrypt(Properties.Settings.Default.Password);

			bool l_bResult = Executer.Connect(false);

			if (l_bResult)
			{
				SetState(EHighlearnerState.Idle);
				RefreshCoursesList();
			}
			else
			{
				SetState(EHighlearnerState.Disconnected);
				ReportErrorConnectingFailed();
			}

			// 			// For Testing
			// 			if (CUtilities.DEBUG_COURSE)
			// 			{
			// 				TestingUtilites.SimulateCourseLoading(Executer, "דוגמא", "Dump.dump");
			// 				TestingUtilites.SimulateCourseLoading(Executer, "1דוגמא", "Dump.dump");
			// 				TestingUtilites.SimulateCourseLoading(Executer, "2דוגמא", "Dump.dump");
			// 
			// 				return;
			// 			}

		}
		public void Connect()
		{
			if (HighlearnerState == EHighlearnerState.Disconnected)
			{
				if (m_oConnectThread != null)
					if (m_oConnectThread.IsAlive)
						m_oConnectThread.Abort();

				m_oConnectThread = new Thread(new ThreadStart(ConnectThreadFunc));
				m_oConnectThread.Start();
			}
		}

		private void RefreshCoursesListThreadFunc()
		{
			if (HighlearnerState != EHighlearnerState.Idle)
				return;

			SetState(EHighlearnerState.RefreshingCoursesList);

			bool l_bResult = Executer.GetAvailibleCourses();

			if (!l_bResult)
				ReportError(EHighlearnerError.RefreshCoursesListFailed);

			SetState(EHighlearnerState.Idle);
		}
		public void RefreshCoursesList()
		{
			if (HighlearnerState == EHighlearnerState.Idle)
			{
				if (m_oRefreshCoursesListThread != null)
					if (m_oRefreshCoursesListThread.IsAlive)
						m_oRefreshCoursesListThread.Abort();

				m_oRefreshCoursesListThread = new Thread(new ThreadStart(RefreshCoursesListThreadFunc));
				m_oRefreshCoursesListThread.Start();
			}
		}

		private void RefreshCourseDetailsThreadFunc(object a_sCourseName)
		{
			SetState(EHighlearnerState.RefreshingCourseItems);

			bool l_bResult = Executer.GetCourseInformation((string)a_sCourseName, true);

			if (!l_bResult)
				ReportErrorRefreshCourseDetailsFailed();

			// Move the course updating into here
			// Update data center with the new data

			SetState(EHighlearnerState.Idle);
		}
		public void RefreshCourseDetails(string a_sName)
		{
			if (HighlearnerState != EHighlearnerState.Idle)
				return;
			
			if (m_oRefreshCourseDetailsThread != null)
				if (m_oRefreshCourseDetailsThread.IsAlive)
					m_oRefreshCourseDetailsThread.Abort();

			m_oRefreshCourseDetailsThread = new Thread(new ParameterizedThreadStart(RefreshCourseDetailsThreadFunc));
			m_oRefreshCourseDetailsThread.Start(a_sName);		
		}
		public void RefreshCourseDetails(CCourse a_oCourse)
		{
			RefreshCourseDetails(a_oCourse.Name);
		}

		public void Download()
		{
			SetState(EHighlearnerState.DownloadingCourse);

			FailedFiles.Clear();
			numberOfDownloadedFiles = 0;

			Executer.DownloadAll(true);
		}

		public void Disconnect()
		{
			Executer.Disconnect();
		}
	}
}
